library(Seurat)
Attaching SeuratObject
library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
library(ggplot2)
library(stringr)
library(tibble)
library(patchwork)
library(plotly)
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
DE table
First we load the sister pair DE tables and filter for:
DE_list <- readRDS("~/spinal_cord_paper/data/Gg_ctrl_poly_sis_markers.rds")
for (i in seq(DE_list)) {
DE_list[[i]] <- DE_list[[i]] %>%
arrange(desc(avg_log2FC)) %>%
filter(abs(avg_log2FC) > 0.5) %>%
filter(p_val_adj < 0.01)
}
DE_table <- do.call(rbind, DE_list)
dim(DE_table)
[1] 807 8
delta pct distribution
par(mfrow = c(2,2))
hist(abs(DE_list[[1]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")
hist(abs(DE_list[[2]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")
hist(abs(DE_list[[4]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")
hist(abs(DE_list[[5]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")

Now we filter the DE lists for absolute delta percentage >
0.1.
for (i in seq(DE_list)) {
DE_list[[i]] <- DE_list[[i]] %>%
filter(abs(delta_pct) > 0.1)
}
DE_table <- do.call(rbind, DE_list)
dim(DE_table)
[1] 671 8
Broad clusters
broad_order <- c("progenitors",
"FP",
"RP",
"FP/RP",
"neurons",
"OPC",
"MFOL",
"pericytes",
"microglia",
"blood",
"vasculature"
)
Integrated data
Load the integrated control and poly data.
int_path <- "Gg_ctrl_poly_int_seurat_250723"
my.se <- readRDS(paste0("~/spinal_cord_paper/data/", int_path, ".rds"))
annot_int <- read.csv(list.files("~/spinal_cord_paper/annotations",
pattern = str_remove(int_path, "_seurat_\\d{6}"),
full.names = TRUE))
if(length(table(annot_int$number)) != length(table(my.se$seurat_clusters))) {
stop("Number of clusters must be identical!")
}
# rename for left join
annot_int <- annot_int %>%
mutate(fine = paste(fine, number, sep = "_")) %>%
mutate(number = factor(number, levels = 1:nrow(annot_int))) %>%
rename(seurat_clusters = number)
ord_levels <- annot_int$fine[order(match(annot_int$broad, broad_order))]
# add cluster annotation to meta data
my.se@meta.data <- my.se@meta.data %>%
rownames_to_column("rowname") %>%
left_join(annot_int, by = "seurat_clusters") %>%
mutate(fine = factor(fine, levels = ord_levels)) %>%
mutate(seurat_clusters = factor(seurat_clusters, levels = str_extract(ord_levels, "\\d{1,2}$"))) %>%
column_to_rownames("rowname")
ctrl_poly_int_combined_labels <- readRDS("~/spinal_cord_paper/annotations/ctrl_poly_int_combined_labels.rds")
my.se <- AddMetaData(my.se, ctrl_poly_int_combined_labels)
DimPlot
DimPlot(
my.se,
group.by = "annot_sample",
reduction = "tsne",
label = TRUE,
repel = TRUE
) +
NoLegend()

Cluster order
Get the cluster order from the spearman correlation heatmap of the
control and poly integrated data. Then we filter for the neuronal
clusters only.
corr_heatmap <- readRDS("~/spinal_cord_paper/output/heatmap_spearman_ctrl_poly.rds")
#heatmap order
htmp_order <- data.frame("label" = corr_heatmap[["gtable"]]$grobs[[4]]$label) %>%
mutate(label = str_remove(label, "_int")) %>%
mutate(label_ordered = paste(str_sub(label,6 ,-1), str_sub(label, 1, 4), sep = "_"))
my.se@meta.data <- my.se@meta.data %>%
mutate(annot_sample = factor(annot_sample, levels = htmp_order$label_ordered))
Idents(my.se) <- "annot_sample"
# filter for the neuronal clusters
my.se <- subset(my.se, idents = htmp_order$label_ordered[grepl("neurons|MN|CSF", htmp_order$label_ordered)])
DimPlot(
my.se,
group.by = "annot_sample",
reduction = "tsne",
label = TRUE,
repel = TRUE
) +
NoLegend()

my.se@active.assay <- "RNA"
Dotplot
# Dotplot of sister pair makrers
pl_all <- modplots::mDotPlot2(my.se,
group.by = "annot_sample",
# reverse order of unique genes so number one is on top
features = rev(unique(DE_table$Gene.stable.ID)),
gnames = modplots::gnames,
cols = c("lightgrey", "black")) +
theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=0.5)) +
coord_flip()
pl_all
pdf("~/spinal_cord_paper/figures/Sister_pair_DE_dotplot.pdf", width = 15, height = 32)
pl_all

DE_table$Gene.name[duplicated(DE_table$Gene.stable.ID)]
[1] "HES5" "MAP6" "GNG5" "ST18" "GAD1" "FABP3"
[7] "SYT1" "SLC32A1" "KIF5C" "HMP19" "GALNT9" "VSTM2L"
[13] "HINTW" "DNER" "CRABP-I" "RELN" "PAX2" "NEUROD2"
[19] "CHL1" "LHX1" "NRXN3" "ENSGALG00000029521" "BHLHE22" "SPOCK1"
[25] "SSTR1" "SLC32A1" "NCALD" "ID2" "GRIK3" "GAD2"
[31] "PTPRK" "GABRG3" "GAD1" "RUNX1T1" "HPCAL1" "ZEB2"
[37] "GALNT9" "ENSGALG00000013212" "MDK" "ZFPM2" "RELN" "NEUROD6"
[43] "CPLX1" "LAMP5" "WNT5A" "HINTW" "SOX4" "DKK3"
[49] "UNC13B" "ATP1B1" "GALNT17" "RASD1" "ENSGALG00000051980" "PLXNA4"
[55] "DACT2" "DISP3" "MVB12B" "ENSGALG00000054223" "CNTN4" "ZNF423"
[61] "CBLB" "FKBP1B" "CELF2" "EPB41L4A" "PXYLP1" "ENSGALG00000023640"
[67] "CNTN2" "MRPS6" "PPP3CA" "NFIX" "NFIA" "SOX8"
[73] "DRAXIN" "CRABP-I" "NHLH1" "TAC1" "VSTM2L" "CPNE2"
[79] "PRKCA"
Individual dot plots
names(DE_list)[3]
[1] "MN_20_ctrl-vs-MN_22_poly"
pdf("~/spinal_cord_paper/figures/Supp_Fig_5_ctrl_poly_dotplot_individual.pdf", height = 21, width = 7)
# without labels for proper alignment
(p1 + p2 + plot_layout(guides = "collect")) /
(p3 + (p4 / p5) + plot_layout(guides = "collect")) &
theme(axis.text.x = element_blank(),
axis.text.y = element_blank())
# with labels to transfer in illustrator
(p1 + p2 + plot_layout(guides = "collect")) /
(p3 + (p4 / p5) + plot_layout(guides = "collect"))
dev.off()
null device
1
Volcanoplots
p.adj <- 0.01
l2fc <- 0
# select top50 by log2FC
for (i in seq(DE_list)) {
DE_list[[i]] <- DE_list[[i]] %>%
mutate(delta_pct_sign = case_when(
delta_pct < 0 ~ "-",
delta_pct > 0 ~ "+",
delta_pct == 0 ~ "0"
))
}
toplot <- do.call(rbind, DE_list[c(4,1)]) %>%
rownames_to_column("contrast") %>%
mutate(contrast = str_remove(contrast, "\\.\\d{1,2}")) %>%
mutate(contrast = str_replace_all(contrast, " ", "_"))
volplot <- ggplot(data = toplot,
aes(x = avg_log2FC,
y = -log10(p_val_adj),
label = Gene.name,
color = delta_pct_sign,
size = abs(delta_pct)
)) +
geom_point(shape = 21) +
geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
scale_color_manual(values = c("goldenrod3", "black")) +
scale_size_continuous(range = c(0.5, 4)) +
facet_wrap("contrast", ncol = 1, scales = "free_y") +
ylab("-log10(padj)") +
theme_bw()
ggplotly(volplot)
NA
pdf("~/spinal_cord_paper/figures/Fig_5_volcanoplots.pdf", width = 5, height = 10)
(volplot +
ggrepel::geom_text_repel(size = 3, color = "black"))
Warning: ggrepel: 13 unlabeled data points (too many overlaps). Consider increasing max.overlaps
Specific markers
Find Markers for clusters 11_ctrl, 16_ctrl, and 15_poly.
gnames <- modplots::gnames
markers <- list()
clu <- c("inhibitory_neurons_16_ctrl",
"excitatory neurons_11_ctrl",
"excitatory_neurons_15_poly")
for (i in seq(clu)) {
markers[[i]] <- FindMarkers(
my.se,
ident.1 = clu[i],
group.by = "annot_sample",
assay = "RNA",
verbose = FALSE,
only.pos = TRUE, # we look for overexpressed, specific markers
min.pct = 0.25,
logfc.threshold = 0.25,
latent.vars = c("CC.Difference.seurat"),
test.use = "MAST"
) %>%
tibble::rownames_to_column("Gene.stable.ID") %>%
dplyr::left_join(gnames, by = "Gene.stable.ID") %>%
dplyr::arrange(-avg_log2FC) %>%
dplyr::filter(p_val_adj < 0.05) %>%
dplyr::filter(abs(avg_log2FC) > 0.5) %>%
dplyr::mutate(delta_pct = abs(pct.1 - pct.2))
}
names(markers) <- clu
Specific marker dotplot
Plot the top 50 markers for clusters 11_ctrl, 16_ctrl, and
15_poly.
n <- 50
mark_plot <- list()
for (i in seq(clu)) {
mark_plot[[i]] <- modplots::mDotPlot2(my.se,
group.by = "annot_sample",
# reverse order of markers so number one is on top
features = rev(markers[[i]][1:n,"Gene.stable.ID"]),
gnames = modplots::gnames) +
theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=0.5)) +
coord_flip() +
scale_colour_gradientn(colours = c("gray90","gray80","yellow", "orange", "red", "darkred", "darkred")) +
ggtitle(paste0("Top ", n, " markers by log2FC for ", clu[i]))
}
mark_plot[[1]]
mark_plot[[2]]
mark_plot[[3]]
pdf("~/spinal_cord_paper/figures/Sister_pair_neuron_marker_dotplots.pdf", width = 14, height = n/3)
mark_plot[[1]]
mark_plot[[2]]
mark_plot[[3]]
# Date and time of Rendering
Sys.time()
sessionInfo()
LS0tCnRpdGxlOiAiU2lzdGVyIHBhaXIgREUgYW5hbHlzaXMsIGRvcGxvdHMgYW5kIHZvbGNhbm9wbG90cyBjdHJsIC12cy0gcG9seSIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMTguMDkuMjAyMyIKZGF0YToKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgaHRtbF9ub3RlYm9vazoKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogOAplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShwbG90bHkpCmBgYAoKIyBERSB0YWJsZQoKRmlyc3Qgd2UgbG9hZCB0aGUgc2lzdGVyIHBhaXIgREUgdGFibGVzIGFuZCBmaWx0ZXIgZm9yOgoKLSAgIGFic29sdXRlIGF2Z19sb2cyRkMgXD4gMC41IChcfjQxJSBpbmNyZWFzZSkKCi0gICBwX3ZhbF9hZGogXDwgMC4wMQoKYGBge3IgREUtZGF0YX0KREVfbGlzdCA8LSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvR2dfY3RybF9wb2x5X3Npc19tYXJrZXJzLnJkcyIpCgpmb3IgKGkgaW4gc2VxKERFX2xpc3QpKSB7CiAgICBERV9saXN0W1tpXV0gPC0gREVfbGlzdFtbaV1dICU+JSAKICAgIGFycmFuZ2UoZGVzYyhhdmdfbG9nMkZDKSkgJT4lIAogICAgZmlsdGVyKGFicyhhdmdfbG9nMkZDKSA+IDAuNSkgJT4lIAogICAgZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDEpCn0KCkRFX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIERFX2xpc3QpCmRpbShERV90YWJsZSkKYGBgCgojIyBkZWx0YSBwY3QgZGlzdHJpYnV0aW9uCgpgYGB7ciBkZWx0YS1wY3QtaGlzdG9ncmFtc30KcGFyKG1mcm93ID0gYygyLDIpKQpoaXN0KGFicyhERV9saXN0W1sxXV0kZGVsdGFfcGN0KSwgYnJlYWtzID0gMjApCmFibGluZSh2ID0gMC4xLCBsdHkgPSAiZGFzaGVkIiwgY29sID0gInJlZCIpCmhpc3QoYWJzKERFX2xpc3RbWzJdXSRkZWx0YV9wY3QpLCBicmVha3MgPSAyMCkKYWJsaW5lKHYgPSAwLjEsIGx0eSA9ICJkYXNoZWQiLCBjb2wgPSAicmVkIikKaGlzdChhYnMoREVfbGlzdFtbNF1dJGRlbHRhX3BjdCksIGJyZWFrcyA9IDIwKQphYmxpbmUodiA9IDAuMSwgbHR5ID0gImRhc2hlZCIsIGNvbCA9ICJyZWQiKQpoaXN0KGFicyhERV9saXN0W1s1XV0kZGVsdGFfcGN0KSwgYnJlYWtzID0gMjApCmFibGluZSh2ID0gMC4xLCBsdHkgPSAiZGFzaGVkIiwgY29sID0gInJlZCIpCmBgYAoKTm93IHdlIGZpbHRlciB0aGUgREUgbGlzdHMgZm9yIGFic29sdXRlIGRlbHRhIHBlcmNlbnRhZ2UgXD4gMC4xLiAKCmBgYHtyIGZpbHRlci1kZWx0YS1wY3R9CmZvciAoaSBpbiBzZXEoREVfbGlzdCkpIHsKICBERV9saXN0W1tpXV0gPC0gREVfbGlzdFtbaV1dICU+JSAKICBmaWx0ZXIoYWJzKGRlbHRhX3BjdCkgPiAwLjEpCn0KCkRFX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIERFX2xpc3QpCmRpbShERV90YWJsZSkKYGBgCgojIEJyb2FkIGNsdXN0ZXJzCgpgYGB7ciBjbHVzdGVyLW9yZGVyfQpicm9hZF9vcmRlciA8LSBjKCJwcm9nZW5pdG9ycyIsCiAgICAgICJGUCIsCiAgICAgICJSUCIsCiAgICAgICJGUC9SUCIsCiAgICAgICJuZXVyb25zIiwKICAgICAgIk9QQyIsCiAgICAgICJNRk9MIiwKICAgICAgInBlcmljeXRlcyIsCiAgICAgICJtaWNyb2dsaWEiLAogICAgICAiYmxvb2QiLAogICAgICAidmFzY3VsYXR1cmUiCiAgICAgICkKCmBgYAoKIyBJbnRlZ3JhdGVkIGRhdGEKCkxvYWQgdGhlIGludGVncmF0ZWQgY29udHJvbCBhbmQgcG9seSBkYXRhLgoKYGBge3IgaW50ZWdyYXRlZC1kYXRhLXBvbHl9CmludF9wYXRoIDwtICJHZ19jdHJsX3BvbHlfaW50X3NldXJhdF8yNTA3MjMiCgpteS5zZSA8LSByZWFkUkRTKHBhc3RlMCgifi9zcGluYWxfY29yZF9wYXBlci9kYXRhLyIsIGludF9wYXRoLCAiLnJkcyIpKQogIGFubm90X2ludCA8LSByZWFkLmNzdihsaXN0LmZpbGVzKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBzdHJfcmVtb3ZlKGludF9wYXRoLCAiX3NldXJhdF9cXGR7Nn0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUUlVFKSkKICAKICBpZihsZW5ndGgodGFibGUoYW5ub3RfaW50JG51bWJlcikpICE9IGxlbmd0aCh0YWJsZShteS5zZSRzZXVyYXRfY2x1c3RlcnMpKSkgewogICAgIHN0b3AoIk51bWJlciBvZiBjbHVzdGVycyBtdXN0IGJlIGlkZW50aWNhbCEiKQogIH0KICAKICAjIHJlbmFtZSBmb3IgbGVmdCBqb2luCiAgYW5ub3RfaW50IDwtIGFubm90X2ludCAlPiUgCiAgICBtdXRhdGUoZmluZSA9IHBhc3RlKGZpbmUsIG51bWJlciwgc2VwID0gIl8iKSkgJT4lIAogICAgbXV0YXRlKG51bWJlciA9IGZhY3RvcihudW1iZXIsIGxldmVscyA9IDE6bnJvdyhhbm5vdF9pbnQpKSkgJT4lIAogICAgcmVuYW1lKHNldXJhdF9jbHVzdGVycyA9IG51bWJlcikKICAKICBvcmRfbGV2ZWxzIDwtIGFubm90X2ludCRmaW5lW29yZGVyKG1hdGNoKGFubm90X2ludCRicm9hZCwgYnJvYWRfb3JkZXIpKV0KICAgCiAgIyBhZGQgY2x1c3RlciBhbm5vdGF0aW9uIHRvIG1ldGEgZGF0YQogIG15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lIAogICAgbGVmdF9qb2luKGFubm90X2ludCwgYnkgPSAic2V1cmF0X2NsdXN0ZXJzIikgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBmYWN0b3IoZmluZSwgbGV2ZWxzID0gb3JkX2xldmVscykpICU+JSAKICAgIG11dGF0ZShzZXVyYXRfY2x1c3RlcnMgPSBmYWN0b3Ioc2V1cmF0X2NsdXN0ZXJzLCBsZXZlbHMgPSBzdHJfZXh0cmFjdChvcmRfbGV2ZWxzLCAiXFxkezEsMn0kIikpKSAlPiUgCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoInJvd25hbWUiKQogIAogIGN0cmxfcG9seV9pbnRfY29tYmluZWRfbGFiZWxzIDwtIHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMvY3RybF9wb2x5X2ludF9jb21iaW5lZF9sYWJlbHMucmRzIikKICAKICBteS5zZSA8LSBBZGRNZXRhRGF0YShteS5zZSwgY3RybF9wb2x5X2ludF9jb21iaW5lZF9sYWJlbHMpCiAgCmBgYAoKIyBEaW1QbG90CgpgYGB7ciBkaW1wbG90fQpEaW1QbG90KAogIG15LnNlLAogIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUKICApICsKICBOb0xlZ2VuZCgpCgpgYGAKCiMgQ2x1c3RlciBvcmRlcgoKR2V0IHRoZSBjbHVzdGVyIG9yZGVyIGZyb20gdGhlIHNwZWFybWFuIGNvcnJlbGF0aW9uIGhlYXRtYXAgb2YgdGhlIGNvbnRyb2wgYW5kIHBvbHkgaW50ZWdyYXRlZCBkYXRhLiBUaGVuIHdlIGZpbHRlciBmb3IgdGhlIG5ldXJvbmFsIGNsdXN0ZXJzIG9ubHkuCgpgYGB7ciBmYWN0b3Itb3JkZXJ9CmNvcnJfaGVhdG1hcCA8LSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL291dHB1dC9oZWF0bWFwX3NwZWFybWFuX2N0cmxfcG9seS5yZHMiKQoKI2hlYXRtYXAgb3JkZXIKaHRtcF9vcmRlciA8LSBkYXRhLmZyYW1lKCJsYWJlbCIgPSBjb3JyX2hlYXRtYXBbWyJndGFibGUiXV0kZ3JvYnNbWzRdXSRsYWJlbCkgJT4lIAogIG11dGF0ZShsYWJlbCA9IHN0cl9yZW1vdmUobGFiZWwsICJfaW50IikpICU+JSAKICBtdXRhdGUobGFiZWxfb3JkZXJlZCA9IHBhc3RlKHN0cl9zdWIobGFiZWwsNiAsLTEpLCBzdHJfc3ViKGxhYmVsLCAxLCA0KSwgc2VwID0gIl8iKSkKCm15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lCiAgbXV0YXRlKGFubm90X3NhbXBsZSA9IGZhY3Rvcihhbm5vdF9zYW1wbGUsIGxldmVscyA9IGh0bXBfb3JkZXIkbGFiZWxfb3JkZXJlZCkpCgpJZGVudHMobXkuc2UpIDwtICJhbm5vdF9zYW1wbGUiCgojIGZpbHRlciBmb3IgdGhlIG5ldXJvbmFsIGNsdXN0ZXJzCm15LnNlIDwtIHN1YnNldChteS5zZSwgaWRlbnRzID0gaHRtcF9vcmRlciRsYWJlbF9vcmRlcmVkW2dyZXBsKCJuZXVyb25zfE1OfENTRiIsIGh0bXBfb3JkZXIkbGFiZWxfb3JkZXJlZCldKQoKRGltUGxvdCgKICBteS5zZSwKICBncm91cC5ieSA9ICJhbm5vdF9zYW1wbGUiLAogIHJlZHVjdGlvbiA9ICJ0c25lIiwKICBsYWJlbCA9IFRSVUUsCiAgcmVwZWwgPSBUUlVFCiAgKSArCiAgTm9MZWdlbmQoKQoKbXkuc2VAYWN0aXZlLmFzc2F5IDwtICJSTkEiCgpgYGAKCiMgRG90cGxvdAoKYGBge3IgYWxsX0RFX2RvdHBsb3QsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0zMH0KCiMgRG90cGxvdCBvZiBzaXN0ZXIgcGFpciBtYWtyZXJzCnBsX2FsbCA8LSBtb2RwbG90czo6bURvdFBsb3QyKG15LnNlLAogICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsIAogICAgICAgICAgICAgICAgICAgICAgIyByZXZlcnNlIG9yZGVyIG9mIHVuaXF1ZSBnZW5lcyBzbyBudW1iZXIgb25lIGlzIG9uIHRvcAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmV2KHVuaXF1ZShERV90YWJsZSRHZW5lLnN0YWJsZS5JRCkpLAogICAgICAgICAgICAgICAgICAgIGduYW1lcyA9IG1vZHBsb3RzOjpnbmFtZXMsCiAgICAgICAgICBjb2xzID0gYygibGlnaHRncmV5IiwgImJsYWNrIikpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0PTEsIHZqdXN0PTAuNSkpICsKICAgIGNvb3JkX2ZsaXAoKQoKcGxfYWxsCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9TaXN0ZXJfcGFpcl9ERV9kb3RwbG90LnBkZiIsIHdpZHRoID0gMTUsIGhlaWdodCA9IDMyKQogIHBsX2FsbAoKICAKREVfdGFibGUkR2VuZS5uYW1lW2R1cGxpY2F0ZWQoREVfdGFibGUkR2VuZS5zdGFibGUuSUQpXQpgYGAKCiMgSW5kaXZpZHVhbCBkb3QgcGxvdHMKCmBgYHtyIGluZGl2aWR1YWxfREVfZG90cGxvdH0KCiMgc2VsZWN0IHRvcDUwIGJ5IGxvZzJGQyAKZm9yIChpIGluIHNlcShERV9saXN0KSkgewogICAgREVfbGlzdFtbaV1dIDwtIERFX2xpc3RbW2ldXSAlPiUKICAgIHNsaWNlX21heChvcmRlcl9ieSA9IGFicyhhdmdfbG9nMkZDKSwgbiA9IDUwKSAlPiUgCiAgICBhcnJhbmdlKGRlc2MoYXZnX2xvZzJGQykpCn0KCnAxIDwtIG1vZHBsb3RzOjptRG90UGxvdDIobXkuc2UsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiYW5ub3Rfc2FtcGxlIiwgCiAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICMgcmV2ZXJzZSBvcmRlciBvZiBERSBnZW5lcyBzbyBudW1iZXIgb25lIGlzIG9uIHRvcAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmV2KERFX2xpc3RbWzFdXSRHZW5lLnN0YWJsZS5JRCksCiAgICAgICAgICAgICAgICAgICAgZ25hbWVzID0gbW9kcGxvdHM6OmduYW1lcywKICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGdyZXkiLCAiYmxhY2siKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICB4bGFiKG5hbWVzKERFX2xpc3QpWzFdKSArCiAgICB5bGFiKGVsZW1lbnRfYmxhbmsoKSkKCnAyIDwtIG1vZHBsb3RzOjptRG90UGxvdDIobXkuc2UsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiYW5ub3Rfc2FtcGxlIiwgIAogICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAjIHJldmVyc2Ugb3JkZXIgb2YgREUgZ2VuZXMgc28gbnVtYmVyIG9uZSBpcyBvbiB0b3AKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IHJldihERV9saXN0W1syXV0kR2VuZS5zdGFibGUuSUQpLAogICAgICAgICAgICAgICAgICAgIGduYW1lcyA9IG1vZHBsb3RzOjpnbmFtZXMsCiAgICAgICAgICBjb2xzID0gYygibGlnaHRncmV5IiwgImJsYWNrIikpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgeGxhYihuYW1lcyhERV9saXN0KVsyXSkgKwogICAgeWxhYihlbGVtZW50X2JsYW5rKCkpCgpwMyA8LSBtb2RwbG90czo6bURvdFBsb3QyKG15LnNlLAogICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsICAKICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgIyByZXZlcnNlIG9yZGVyIG9mIERFIGdlbmVzIHNvIG51bWJlciBvbmUgaXMgb24gdG9wCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSByZXYoREVfbGlzdFtbNV1dJEdlbmUuc3RhYmxlLklEKSwKICAgICAgICAgICAgICAgICAgICBnbmFtZXMgPSBtb2RwbG90czo6Z25hbWVzLAogICAgICAgICAgY29scyA9IGMoImxpZ2h0Z3JleSIsICJibGFjayIpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHhsYWIobmFtZXMoREVfbGlzdClbNV0pICsKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKQoKcDQgPC0gbW9kcGxvdHM6Om1Eb3RQbG90MihteS5zZSwKICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJhbm5vdF9zYW1wbGUiLCAgCiAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICMgcmV2ZXJzZSBvcmRlciBvZiBERSBnZW5lcyBzbyBudW1iZXIgb25lIGlzIG9uIHRvcAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmV2KERFX2xpc3RbWzRdXSRHZW5lLnN0YWJsZS5JRCksCiAgICAgICAgICAgICAgICAgICAgZ25hbWVzID0gbW9kcGxvdHM6OmduYW1lcywKICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGdyZXkiLCAiYmxhY2siKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHhsYWIobmFtZXMoREVfbGlzdClbNF0pICsKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKQoKcDUgPC0gbW9kcGxvdHM6Om1Eb3RQbG90MihteS5zZSwKICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJhbm5vdF9zYW1wbGUiLCAgCiAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICMgUkVMTiAodG8gZW5zdXJlIHByb3BlciBzY2FsaW5nKSBhbmQKICAgICAgICAgICAgICAgICAgICAgICMgTkQzIChmaWx0ZXIgb3V0IHByZXZpb3VzbHkgZHVlIHRvIGRlbHRhX3BjdCA9IDAuMDA5KQogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiRU5TR0FMRzAwMDAwMDA4MTkzIiwgIkVOU0dBTEcwMDAwMDAzMDQzNiIpLAogICAgICAgICAgICAgICAgICAgIGduYW1lcyA9IG1vZHBsb3RzOjpnbmFtZXMsCiAgICAgICAgICBjb2xzID0gYygibGlnaHRncmV5IiwgImJsYWNrIikpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0PTEsIHZqdXN0PTAuNSkpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICB4bGFiKG5hbWVzKERFX2xpc3QpWzNdKSArCiAgICB5bGFiKGVsZW1lbnRfYmxhbmsoKSkKCmBgYAoKYGBge3IgZXhwb3J0X3Bsb3RzIH0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL1N1cHBfRmlnXzVfY3RybF9wb2x5X2RvdHBsb3RfaW5kaXZpZHVhbC5wZGYiLCBoZWlnaHQgPSAyMSwgd2lkdGggPSA3KQojIHdpdGhvdXQgbGFiZWxzIGZvciBwcm9wZXIgYWxpZ25tZW50CihwMSArIHAyICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSkgLwoocDMgKyAocDQgLyBwNSkgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpKSAmIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQojIHdpdGggbGFiZWxzIHRvIHRyYW5zZmVyIGluIGlsbHVzdHJhdG9yCihwMSArIHAyICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSkgLwoocDMgKyAocDQgLyBwNSkgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpKQoKZGV2Lm9mZigpCmBgYAoKIyBWb2xjYW5vcGxvdHMKCmBgYHtyIHZvbGNhbm9wbG90cywgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTd9CnAuYWRqIDwtIDAuMDEKbDJmYyA8LSAwCgojIHNlbGVjdCB0b3A1MCBieSBsb2cyRkMgCmZvciAoaSBpbiBzZXEoREVfbGlzdCkpIHsKICAgIERFX2xpc3RbW2ldXSA8LSBERV9saXN0W1tpXV0gJT4lIAogICAgbXV0YXRlKGRlbHRhX3BjdF9zaWduID0gY2FzZV93aGVuKAogICAgICBkZWx0YV9wY3QgPCAwIH4gIi0iLAogICAgICBkZWx0YV9wY3QgPiAwIH4gIisiLAogICAgICBkZWx0YV9wY3QgPT0gMCB+ICIwIgogICAgKSkKfQogCgp0b3Bsb3QgPC0gZG8uY2FsbChyYmluZCwgREVfbGlzdFtjKDQsMSldKSAlPiUgCiAgcm93bmFtZXNfdG9fY29sdW1uKCJjb250cmFzdCIpICU+JSAKICBtdXRhdGUoY29udHJhc3QgPSBzdHJfcmVtb3ZlKGNvbnRyYXN0LCAiXFwuXFxkezEsMn0iKSkgJT4lIAogIG11dGF0ZShjb250cmFzdCA9IHN0cl9yZXBsYWNlX2FsbChjb250cmFzdCwgIiAiLCAiXyIpKQoKdm9scGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IHRvcGxvdCwKICAgICAgIGFlcyh4ID0gYXZnX2xvZzJGQywKICAgICAgICAgICB5ID0gLWxvZzEwKHBfdmFsX2FkaiksCiAgICAgICAgICAgbGFiZWwgPSBHZW5lLm5hbWUsCiAgICAgICAgICAgY29sb3IgPSBkZWx0YV9wY3Rfc2lnbiwKICAgICAgICAgICBzaXplID0gYWJzKGRlbHRhX3BjdCkKICAgICAgICkpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMjEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAocC5hZGopLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtbDJmYyxsMmZjKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJnb2xkZW5yb2QzIiwgImJsYWNrIikpICsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDAuNSwgNCkpICsKICBmYWNldF93cmFwKCJjb250cmFzdCIsIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpCgpnZ3Bsb3RseSh2b2xwbG90KQoKYGBgCgpgYGB7cn0KcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzVfdm9sY2Fub3Bsb3RzLnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMTApCih2b2xwbG90ICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikpCgpgYGAKIAojIFNwZWNpZmljIG1hcmtlcnMKCkZpbmQgTWFya2VycyBmb3IgY2x1c3RlcnMgMTFfY3RybCwgMTZfY3RybCwgYW5kIDE1X3BvbHkuCgpgYGB7ciBzcGVjaWZpYy1tYXJrZXJzfQpnbmFtZXMgPC0gbW9kcGxvdHM6OmduYW1lcwoKbWFya2VycyA8LSBsaXN0KCkKCmNsdSA8LSBjKCJpbmhpYml0b3J5X25ldXJvbnNfMTZfY3RybCIsCiAgICAgICAgICJleGNpdGF0b3J5IG5ldXJvbnNfMTFfY3RybCIsCiAgICAgICAgICJleGNpdGF0b3J5X25ldXJvbnNfMTVfcG9seSIpCgpmb3IgKGkgaW4gc2VxKGNsdSkpIHsgIAogIG1hcmtlcnNbW2ldXSA8LSBGaW5kTWFya2VycygKICAgICAgbXkuc2UsCiAgICAgIGlkZW50LjEgPSBjbHVbaV0sCiAgICAgIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsCiAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgICAgb25seS5wb3MgPSBUUlVFLCAjIHdlIGxvb2sgZm9yIG92ZXJleHByZXNzZWQsIHNwZWNpZmljIG1hcmtlcnMKICAgICAgbWluLnBjdCA9IDAuMjUsCiAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9ICAwLjI1LAogICAgICBsYXRlbnQudmFycyA9IGMoIkNDLkRpZmZlcmVuY2Uuc2V1cmF0IiksCiAgICAgIHRlc3QudXNlID0gIk1BU1QiCiAgICApICU+JQogICAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiR2VuZS5zdGFibGUuSUQiKSAlPiUKICAgICAgZHBseXI6OmxlZnRfam9pbihnbmFtZXMsIGJ5ID0gIkdlbmUuc3RhYmxlLklEIikgJT4lCiAgICAgIGRwbHlyOjphcnJhbmdlKC1hdmdfbG9nMkZDKSAlPiUKICAgICAgZHBseXI6OmZpbHRlcihwX3ZhbF9hZGogPCAwLjA1KSAlPiUKICAgICAgZHBseXI6OmZpbHRlcihhYnMoYXZnX2xvZzJGQykgPiAwLjUpICU+JQogICAgZHBseXI6Om11dGF0ZShkZWx0YV9wY3QgPSBhYnMocGN0LjEgLSBwY3QuMikpCn0KCm5hbWVzKG1hcmtlcnMpIDwtIGNsdQpgYGAKCiMgU3BlY2lmaWMgbWFya2VyIGRvdHBsb3QKClBsb3QgdGhlIHRvcCA1MCBtYXJrZXJzIGZvciBjbHVzdGVycyAxMV9jdHJsLCAxNl9jdHJsLCBhbmQgMTVfcG9seS4KCmBgYHtyIG5ldXJvbi1tYXJrZXItZG90cGxvdHMsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9Cm4gPC0gNTAKCm1hcmtfcGxvdCA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEoY2x1KSkgewogIG1hcmtfcGxvdFtbaV1dIDwtIG1vZHBsb3RzOjptRG90UGxvdDIobXkuc2UsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiYW5ub3Rfc2FtcGxlIiwgCiAgICAgICAgICAgICAgICAgICAgICAjIHJldmVyc2Ugb3JkZXIgb2YgbWFya2VycyBzbyBudW1iZXIgb25lIGlzIG9uIHRvcAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmV2KG1hcmtlcnNbW2ldXVsxOm4sIkdlbmUuc3RhYmxlLklEIl0pLAogICAgICAgICAgICAgICAgICAgIGduYW1lcyA9IG1vZHBsb3RzOjpnbmFtZXMpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0PTEsIHZqdXN0PTAuNSkpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzID0gYygiZ3JheTkwIiwiZ3JheTgwIiwieWVsbG93IiwgIm9yYW5nZSIsICJyZWQiLCAiZGFya3JlZCIsICJkYXJrcmVkIikpICsKICBnZ3RpdGxlKHBhc3RlMCgiVG9wICIsIG4sICIgbWFya2VycyBieSBsb2cyRkMgZm9yICIsIGNsdVtpXSkpCgp9CgptYXJrX3Bsb3RbWzFdXQptYXJrX3Bsb3RbWzJdXQptYXJrX3Bsb3RbWzNdXQoKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL1Npc3Rlcl9wYWlyX25ldXJvbl9tYXJrZXJfZG90cGxvdHMucGRmIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gbi8zKQptYXJrX3Bsb3RbWzFdXQptYXJrX3Bsb3RbWzJdXQptYXJrX3Bsb3RbWzNdXQoKYGBgCgpgYGB7ciBTZXNzaW9uLWluZm99CiMgRGF0ZSBhbmQgdGltZSBvZiBSZW5kZXJpbmcKU3lzLnRpbWUoKQoKc2Vzc2lvbkluZm8oKQpgYGAK